
#include "lib_ffmpeg.h"
//****************************************************************





//****************************************************************

static SDL_mutex *	    ffmpeg_mutex = NULL;

static pthread_t  		pRead_frame_thread;
static pthread_t  		pAudio_decode_thread;
static AVFormatContext* ffmpeg_avformat = NULL;
static AVCodecContext*  pCodecCtx_audio = NULL;
static AVCodecContext*  pCodecCtx_video = NULL;
static struct AVCodec*  codec_audio 	= NULL;
static struct AVCodec*  codec_video 	= NULL;
static AVPacket*        ffmpeg_avPacket	= NULL;
static AVPacket*        audio_packet	= NULL;

static AVFrame* 		   audio_frame     = NULL;
static  struct SwrContext* au_convert_ctx  = NULL;

static  uint8_t* pcm_data_buff = NULL;

static int	ffmpeg_run_state   = FFMPEG_RUN_FIND_FILE;
static int	ffmpeg_video_index = -1;
static int	ffmpeg_audio_index = -1;
static int	ffmpeg_video_w 	   = 0;
static int	ffmpeg_video_h 	   = 0;

static int	ffmpeg_start_decode    = 0;
static int  ffmpeg_pcm_buffer_size = 0;

static int  ffmpeg_index  		   = 1;

static int  ffmpeg_message  	   = 0;
static int  ffmpeg_fps      	   = 0;

//****************************************************************
int  ffmpeg_get_video_w(void)
{
	return ffmpeg_video_w;
}

int  ffmpeg_get_video_h(void)
{
	return ffmpeg_video_h;
}

int  ffmpeg_get_video_index(void)
{
	return ffmpeg_video_index;
}

int  ffmpeg_get_audio_index(void)
{
	return ffmpeg_audio_index;
}



int ffmpeg_get_extradata(uint8_t**ex)
{	
	log_d("video_index=%d\n",ffmpeg_video_index);
	
	log_d("ffmpeg_avformat=%x\n",ffmpeg_avformat);
	log_d("streams=%x\n",ffmpeg_avformat->streams[ffmpeg_video_index]);
	log_d("codecpar=%x\n",ffmpeg_avformat->streams[ffmpeg_video_index]->codecpar);
	log_d("extradata=%x\n",ffmpeg_avformat->streams[ffmpeg_video_index]->codecpar->extradata);

	*ex = ffmpeg_avformat->streams[ffmpeg_video_index]->codecpar->extradata;

	log_d("\n");

	return 0;
}

int  ffmpeg_get_decode_flag(void)
{
	return ffmpeg_start_decode;
}


int  ffmpeg_get_sample_rate(void)
{
	return pCodecCtx_audio->sample_rate;
}

int  ffmpeg_get_channels(void)
{
	return pCodecCtx_audio->channels;
}

int  ffmpeg_get_sample_fmt(void)
{
	return pCodecCtx_audio->sample_fmt;
}

int  ffmpeg_get_frame_size(void)
{
	return pCodecCtx_audio->frame_size;
}

int ffmpeg_get_video_time_base(int* num, int* den)
{
	if(!num)
		return -1;
	
	if(!den)
		return -1;	

	*num = ffmpeg_avformat->streams[ffmpeg_video_index]->time_base.num;
	*den = ffmpeg_avformat->streams[ffmpeg_video_index]->time_base.den;

	return 0;
}

int  ffmpeg_get_audio_time_base(int* num, int* den)
{
    if(!num)
		return -1;
	
    if(!den)
		return -1;	

	*num = ffmpeg_avformat->streams[ffmpeg_audio_index]->time_base.num;
	*den = ffmpeg_avformat->streams[ffmpeg_audio_index]->time_base.den;

	return 0;
}

int  ffmpeg_get_fps(int* fps)
{
    if(!fps)
		return -1;
	
	*fps = ffmpeg_fps;

	return 0;
}



int  ffmpeg_set_msg(int msg)
{
	ffmpeg_message = msg;

	return 0;
}

//****************************************************************
static int ffmpeg_open_stream(const char *file_name)
{
	int i    = 0;
	int ret  = 0;
	int sample_fmt  = 0;
	int hours, mins, secs, us;
	int64_t   duration = 0;
	int64_t   in_channel_layout  = 0;

	
    AVDictionary * opts = NULL;
	AVDictionary * dict         = NULL;
	AVStream *     video_stream = NULL;

	
	SDL_LockMutex(ffmpeg_mutex);

	av_dict_set(&dict, "rtsp_transport", "tcp", 0);

	ffmpeg_avformat = avformat_alloc_context();
	if(!ffmpeg_avformat){
		log_e("avformat error\n");
		SDL_UnlockMutex(ffmpeg_mutex);
		return -1;
	}
	//****************************************************************

	ret = avformat_open_input(&ffmpeg_avformat, file_name, NULL, &dict);
	if(ret != 0){
		log_e("open err=%s\n",file_name);
		avformat_free_context(ffmpeg_avformat);
		ffmpeg_avformat = NULL;
		SDL_UnlockMutex(ffmpeg_mutex);
		return -1;
	}
	//****************************************************************
	ret = avformat_find_stream_info(ffmpeg_avformat, NULL);
	if(ret < 0){	
		log_e("no stream\n");
		if(ffmpeg_avformat){
			if(&ffmpeg_avformat){
				avformat_close_input(&ffmpeg_avformat);
			}
			avformat_free_context(ffmpeg_avformat);
			ffmpeg_avformat = NULL;
		}
		SDL_UnlockMutex(ffmpeg_mutex);
		return -1;
	}	
	//****************************************************************

	av_dump_format(ffmpeg_avformat, 0, file_name, 0);

	log_i("duration=%lld\n",ffmpeg_avformat->duration);
	//****************************************************************

	ffmpeg_video_index = -1;
	ffmpeg_audio_index = -1;

	log_i("nb_streams=%d\n",ffmpeg_avformat->nb_streams);
	
	for (i = 0; i < ffmpeg_avformat->nb_streams; i++)
	{
		log_d("codec_type=%d\n",ffmpeg_avformat->streams[i]->codecpar->codec_type);
		
		if (ffmpeg_avformat->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) 
		{
			ffmpeg_video_index = i;
		}
		if (ffmpeg_avformat->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) 
		{
			ffmpeg_audio_index = i;
		}
	}		

	log_i("ffmpeg_video_index =%d\n",ffmpeg_video_index);
	log_i("ffmpeg_audio_index =%d\n",ffmpeg_audio_index);
	//*************************************************************************************
	
	duration = ffmpeg_avformat->duration;

	secs  	 = duration / AV_TIME_BASE;
	us	     = duration % AV_TIME_BASE;
	mins     = secs / 60;
	secs    %= 60;
	hours    = mins / 60;
	mins     %= 60;
	
	log_i("duration=%lld\n",duration);
	log_i("duration=%02d:%02d:%02d.%02d\n",hours,mins,secs,(100 * us) / AV_TIME_BASE);
	//*************************************************************************************
	if(ffmpeg_video_index == -1){
		log_e("no video stream\n");		
		if(ffmpeg_avformat){
			if(&ffmpeg_avformat){
				avformat_close_input(&ffmpeg_avformat);
			}
			avformat_free_context(ffmpeg_avformat);
			ffmpeg_avformat = NULL;
		}
		SDL_UnlockMutex(ffmpeg_mutex);
		return -1;
	}
	//*************************************************************************************
	log_d("codec_id =%d\n",ffmpeg_avformat->streams[ffmpeg_video_index]->codecpar->codec_id);

    pCodecCtx_video = avcodec_alloc_context3(NULL);
    if (!pCodecCtx_video){
		log_e("avcodec alloc error\n");
		return -1;
	}

    ret = avcodec_parameters_to_context(pCodecCtx_video, ffmpeg_avformat->streams[ffmpeg_video_index]->codecpar);
    if (ret < 0){
		log_e("avcodec parameters error\n");
		return -1;
	}

    codec_video = avcodec_find_decoder(pCodecCtx_video->codec_id);
	
	log_d("codec_video =%x\n",codec_video);
	
	if(codec_video){
		log_i("name =%s\n",codec_video->name);
	}

	ffmpeg_video_w  = pCodecCtx_video->width;
	ffmpeg_video_h  = pCodecCtx_video->height;

	
	log_i("video_w =%d\n",ffmpeg_video_w);
	log_i("video_h =%d\n",ffmpeg_video_h);
	//*************************************************************************************
	video_stream = ffmpeg_avformat->streams[ffmpeg_video_index];
	ffmpeg_fps   = video_stream->avg_frame_rate.num/video_stream->avg_frame_rate.den;

	log_i("fps =%d\n",ffmpeg_fps);
	//*************************************************************************************
	//audio info 

	if(ffmpeg_audio_index != -1){

	    pCodecCtx_audio = avcodec_alloc_context3(NULL);
	    if (!pCodecCtx_audio){
			log_e("avcodec alloc error\n");
			return -1;
		}

		pCodecCtx_audio->thread_count = 16;
		
		ret = avcodec_parameters_to_context(pCodecCtx_audio, ffmpeg_avformat->streams[ffmpeg_audio_index]->codecpar);
		if (ret < 0){
			log_e("avcodec parameters error\n");
			return -1;
		}
	
		codec_audio = avcodec_find_decoder(pCodecCtx_audio->codec_id);

		if(!codec_audio){
			log_e("no audio codec.\n");
			return -1;
		}
		
		log_i("audio codec name =%s\n",codec_audio->name);

		av_dict_set(&opts, "threads", "auto", 0);
		av_dict_set(&opts, "refcounted_frames", "1", 0);
		av_dict_set(&opts, "timeout", "1000", 0);//3s

		if(pCodecCtx_audio&&codec_audio){
			ret = avcodec_open2(pCodecCtx_audio, codec_audio, &opts);
			if(ret < 0){
				log_e("audio codec open error.\n");
				return -1;
			}
		}
		
		//AV_CH_LAYOUT_STEREO
		//AV_SAMPLE_FMT_S16

		log_i("frame_size =%d\n",pCodecCtx_audio->frame_size);
		log_i("channels   =%d\n",pCodecCtx_audio->channels);
		log_i("sample_fmt =%d\n",pCodecCtx_audio->sample_fmt);
		log_i("sample_rate=%d\n",pCodecCtx_audio->sample_rate);

	}
	//*************************************************************************************

	ffmpeg_avPacket = av_packet_alloc();
	av_init_packet(ffmpeg_avPacket);


	audio_frame = av_frame_alloc();
	if(!audio_frame){
		log_e("pcm data buff malloc error.\n");
	}

	audio_packet = av_packet_alloc();
	av_init_packet(audio_packet);

	//****************************************************************
	if(pCodecCtx_audio->sample_fmt == 8)
	{
		sample_fmt = AV_SAMPLE_FMT_U8;
	}
	
	if(pCodecCtx_audio->sample_fmt == 16)
	{
		sample_fmt = AV_SAMPLE_FMT_S16;
	}

	if(pCodecCtx_audio->sample_fmt == 32)
	{
		sample_fmt = AV_SAMPLE_FMT_S32;
	}

	sample_fmt =  pCodecCtx_audio->sample_fmt;
	
	in_channel_layout = av_get_default_channel_layout(pCodecCtx_audio->channels);

	au_convert_ctx	   = swr_alloc();
	if(au_convert_ctx){
		au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, 
											AV_CH_LAYOUT_STEREO, 
											AV_SAMPLE_FMT_S16, 
											pCodecCtx_audio->sample_rate,
											in_channel_layout, 
											sample_fmt, 
											pCodecCtx_audio->sample_rate, 0, NULL);
		swr_init(au_convert_ctx);
	}

	pcm_data_buff	        = (uint8_t*)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
	ffmpeg_pcm_buffer_size  = av_samples_get_buffer_size(NULL, 
													av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO), 
													pCodecCtx_audio->frame_size,
													AV_SAMPLE_FMT_S16, 1);
	
	log_i("pcm buff size=%d\n",ffmpeg_pcm_buffer_size);											
	//****************************************************************

	ffmpeg_start_decode = 1;
	
	log_i("open file stream succeed.\n");

	

	//****************************************************************
	SDL_UnlockMutex(ffmpeg_mutex);

	return 0;
}
//****************************************************************
static int ffmpeg_close_stream(void)
{

	SDL_LockMutex(ffmpeg_mutex);

	log_i("close file stream.\n");

	ffmpeg_start_decode = 0;

	if(pCodecCtx_audio){
		avcodec_free_context(&pCodecCtx_audio);
		pCodecCtx_audio = NULL;
	}
	
	if(pCodecCtx_video){
		avcodec_free_context(&pCodecCtx_video);
		pCodecCtx_video = NULL;
	}

	if(pCodecCtx_audio){
		avcodec_close(pCodecCtx_audio);
		pCodecCtx_audio = NULL;
	}

	if(ffmpeg_avPacket){
		av_packet_free(ffmpeg_avPacket);
		ffmpeg_avPacket = NULL;
	}

	
	if(audio_packet){
		av_packet_free(audio_packet);
		audio_packet = NULL;
	}

	if(ffmpeg_avformat)
	{
		if(&ffmpeg_avformat)
			avformat_close_input(&ffmpeg_avformat);
		avformat_free_context(ffmpeg_avformat);
		ffmpeg_avformat = NULL;
	}

	if(audio_frame){
		av_frame_free(&audio_frame);
		audio_frame = NULL;
	}
	
	if(au_convert_ctx){
		swr_free(&au_convert_ctx);
		au_convert_ctx = NULL;
	}	
	
	if(pcm_data_buff){
		av_free(pcm_data_buff);
		pcm_data_buff = NULL;
	}


	
	SDL_UnlockMutex(ffmpeg_mutex);
	
	return 0;

}
//****************************************************************

static int ffmpeg_read_frame(void)
{
	int ret = 0;
	
	char error_msg[256] ={0};
	  
	ret = av_read_frame(ffmpeg_avformat, ffmpeg_avPacket);
	if(ret){
		av_packet_unref(ffmpeg_avPacket);
		memset(error_msg,0,sizeof(error_msg));
		sprintf(error_msg, "msg:%s\n", av_err2str(ret));
		log_e("%s\n",error_msg);
		return -1;
	}

	if(ffmpeg_avPacket->stream_index == ffmpeg_video_index)
	{
		video_mutex_lock();
		queue_packet_write_video(ffmpeg_avPacket->data,ffmpeg_avPacket->size,ffmpeg_avPacket->pts,ffmpeg_avPacket->dts);
		video_mutex_unlock();
	}
	
	if(ffmpeg_avPacket->stream_index == ffmpeg_audio_index)
	{
		audio_mutex_lock();
		queue_packet_write_audio(ffmpeg_avPacket->data,ffmpeg_avPacket->size,ffmpeg_avPacket->pts);	
		audio_mutex_unlock();
	}
	
	av_packet_unref(ffmpeg_avPacket);
	

	return 0;

}

//****************************************************************
int ffmpeg_packet_is_ready(void)
{
	if(ffmpeg_run_state == FFMPEG_RUN_READ_PACKET)
		return 1;
	else if(ffmpeg_run_state == FFMPEG_RUN_IDLE)
		return 1;
	else
		return 0;
}
//****************************************************************

void* ffmpeg_read_frame_thread(void *arg)
{
	int ret = -1;

	char filename[1024] = {0};

	while(1)
	{
		switch(ffmpeg_run_state){
		case FFMPEG_RUN_INIT:
		{
			log_i("\n");
			ret = ffmpeg_open_stream(filename);
			if(ret != 0){
				ffmpeg_run_state = FFMPEG_RUN_IDLE;
			}else{
				ffmpeg_run_state = FFMPEG_RUN_READ_PACKET;
			}

			if((ffmpeg_get_video_w() > MAX_VIDEO_W)||(ffmpeg_get_video_h() > MAX_VIDEO_H))
			{
				ffmpeg_run_state = FFMPEG_RUN_STOP;
			}

			break;
		}
		case FFMPEG_RUN_FIND_FILE:
		{
			memset(filename, 0, sizeof(filename));
			ret = file_get_full_name(filename,ffmpeg_index);
			if(ret != 0){
				break;
			}

			ffmpeg_index++;
			if(ffmpeg_index > file_get_total_num())
			{
				ffmpeg_index = 1;
			}

			log_i("filename=%s\n",filename);

			if(__Valid_VideoFileType(filename))
			{
				log_i("\n");
				ffmpeg_run_state = FFMPEG_RUN_INIT;
				break;
			}
			log_i("\n");
		}
		case FFMPEG_RUN_READ_PACKET:
		{
			if(ffmpeg_message != 0)
			{
				ffmpeg_message = 0;
				ffmpeg_run_state = FFMPEG_RUN_STOP;
				break;
			}

			ret = ffmpeg_read_frame();
			if(ret != 0){
				if(queue_packet_video_is_empty())
				{
					ffmpeg_run_state = FFMPEG_RUN_STOP;
				}
				else
				{
					queue_packet_query_info();
					ffmpeg_run_state = FFMPEG_RUN_IDLE;
				}
			}
			break;
		}			
		case FFMPEG_RUN_PAUSE:
		{

			break;
		}		
		case FFMPEG_RUN_STOP:
		{
			alsa_pcm_buff_clear();

			ffmpeg_close_stream();
			
			queue_packet_deinit();
			queue_audio_pcm_deinit();
			
			mpp_para_deinit();
			
			ffmpeg_run_state = FFMPEG_RUN_FIND_FILE;

			
			break;
		}		
		case FFMPEG_RUN_IDLE:
		{
			if(queue_packet_video_is_empty())
			{
				ffmpeg_run_state = FFMPEG_RUN_STOP;
			}
			
			if(ffmpeg_message != 0)
			{
				ffmpeg_message = 0;
				ffmpeg_run_state = FFMPEG_RUN_STOP;
				break;
			}

			break;
		}
		default:break;
		}	
		usleep(1000);
	}
}


int ffmpeg_decode_pcm_audio(AVCodecContext *pCodecCtx_audio, AVFrame *frame, AVPacket *avpkt)
{
	int ret  = -1;	
	char error_msg[256] ={0};

	long	  us_start	  = 0;
	long	  us_end	  = 0;


	if(!pCodecCtx_audio)
		return -1;
	
	if(!frame)
		return -1;

	if(!avpkt)
		return -1;

	if(!avpkt->data)
		return -1;

	if(!avpkt->size)
		return -1;	


	us_start = platform_get_microsecond();	
	ret = avcodec_send_packet(pCodecCtx_audio, avpkt);
	us_end = platform_get_microsecond();
	//log_i("time1,ret = %d:%d\n",us_end - us_start,ret);

	av_packet_unref(avpkt);

	if (ret < 0){
		memset(error_msg,0,sizeof(error_msg));
		sprintf(error_msg, "error:%s\n", av_err2str(ret));
		log_e("%s\n",error_msg);
		return -1;
	}
	
	ret = 0;
	while(ret == 0)
	{
		ret = avcodec_receive_frame(pCodecCtx_audio, frame);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
		{
			memset(error_msg,0,sizeof(error_msg));
			sprintf(error_msg, "error:%s\n", av_err2str(ret));
			log_e("%s\n",error_msg);
		
			break;
		}
		else if (ret < 0)
		{
			memset(error_msg,0,sizeof(error_msg));
			sprintf(error_msg, "error:%s\n", av_err2str(ret));
			log_e("%s\n",error_msg);

			break;
		}
		else
		{
			if(au_convert_ctx&&pcm_data_buff){
				ret = swr_convert(au_convert_ctx, &pcm_data_buff, MAX_AUDIO_FRAME_SIZE, &frame->data, frame->nb_samples);
			}
		
			if(ret < 0){
				memset(error_msg,0,sizeof(error_msg));
				sprintf(error_msg, "error:%s\n", av_err2str(ret));
				log_e("%s\n",error_msg);
			}else{
				queue_audio_pcm_write(pcm_data_buff,ffmpeg_pcm_buffer_size,frame->pts);
			}
		}
	}

	return 0;
}




void* ffmpeg_audio_decode_thread(void *arg)
{
	int       ret = -1;
	int       packet_size = 0;
	uint8_t*  packet_data = NULL;
	int64_t   audio_pts   = 0;
	long      us_start    = 0;
	long      us_end      = 0;

	while(1)
	{
		usleep(1000);
		
		if(!ffmpeg_get_decode_flag()){
			continue;
		}		

		if(!ffmpeg_packet_is_ready()){
			continue;
		}
		
		audio_mutex_lock();
		
		ret = queue_packet_read_audio(&packet_data,&packet_size,&audio_pts);
		if(ret != 0){
			audio_mutex_unlock();
			continue;
		}
		
		audio_packet->data = packet_data;
		audio_packet->size = packet_size;
		audio_packet->pts  = audio_pts;
	
		ffmpeg_decode_pcm_audio(pCodecCtx_audio,audio_frame,audio_packet);

		queue_packet_read_audio_free();
		
		audio_mutex_unlock();
	}

}

//****************************************************************

int lib_ffmpeg_init(void)
{
	int ret = -1;
	
	log_i("lib ffmpeg init.\n");

	avdevice_register_all();
	avformat_network_init();
	
	ffmpeg_mutex = SDL_CreateMutex();

	ret=pthread_create(&pRead_frame_thread,NULL,ffmpeg_read_frame_thread,NULL);
	if(ret != 0){
		log_e("thread error=%s\n",strerror(ret));
	}

	ret=pthread_create(&pAudio_decode_thread,NULL,ffmpeg_audio_decode_thread,NULL);
	if(ret != 0){
		log_e("thread error=%s\n",strerror(ret));
	}


	return 0;
}

//****************************************************************

int lib_ffmpeg_deinit(void)
{
	log_i("lib ffmpeg exit.\n");

	if(ffmpeg_mutex){
		SDL_DestroyMutex(ffmpeg_mutex);
		ffmpeg_mutex = NULL;
	}
	 



	return 0;
}


//****************************************************************





